package com.ouc.ouc.converter;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.google.common.base.Charsets;
import io.terminus.common.utils.JsonMapper;
import java.io.IOException;
import java.nio.charset.Charset;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.Assert;

public class JsonMessageConverter
  extends AbstractHttpMessageConverter<Object>
{
  public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
  private ObjectMapper objectMapper = JsonMapper.nonEmptyMapper().getMapper();
  private boolean prefixJson = false;
  
  public JsonMessageConverter()
  {
    super(new MediaType[] { new MediaType("application", "json", DEFAULT_CHARSET), new MediaType("application", "javascript", DEFAULT_CHARSET) });
  }
  
  public void setObjectMapper(ObjectMapper objectMapper)
  {
    Assert.notNull(objectMapper, "ObjectMapper must not be null");
    this.objectMapper = objectMapper;
  }
  
  public ObjectMapper getObjectMapper()
  {
    return this.objectMapper;
  }
  
  public void setPrefixJson(boolean prefixJson)
  {
    this.prefixJson = prefixJson;
  }
  
  public boolean canRead(Class<?> clazz, MediaType mediaType)
  {
    JavaType javaType = getJavaType(clazz);
    return (this.objectMapper.canDeserialize(javaType)) && (canRead(mediaType));
  }
  
  public boolean canWrite(Class<?> clazz, MediaType mediaType)
  {
    return (this.objectMapper.canSerialize(clazz)) && (canWrite(mediaType));
  }
  
  protected boolean supports(Class<?> clazz)
  {
    throw new UnsupportedOperationException();
  }
  
  protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
    throws IOException, HttpMessageNotReadableException
  {
    JavaType javaType = getJavaType(clazz);
    try
    {
      return this.objectMapper.readValue(inputMessage.getBody(), javaType);
    }
    catch (IOException ex)
    {
      throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
    }
  }
  
  protected void writeInternal(Object object, HttpOutputMessage outputMessage)
    throws IOException, HttpMessageNotWritableException
  {
    JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
    JsonGenerator jsonGenerator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
    try
    {
      if (this.prefixJson) {
        jsonGenerator.writeRaw("{} && ");
      }
      Object result;
      if ((object instanceof JSONPObject))
      {
        JSONPObject jsonP = (JSONPObject)object;
        result = new JSONPObject(jsonP.getFunction(), jsonP.getValue());
      }
      else
      {
        result = object;
      }
      this.objectMapper.writeValue(jsonGenerator, result);
    }
    catch (IOException ex)
    {
      throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
    }
  }
  
  protected JavaType getJavaType(Class<?> clazz)
  {
    return this.objectMapper.constructType(clazz);
  }
  
  protected JsonEncoding getJsonEncoding(MediaType contentType)
  {
    if ((contentType != null) && (contentType.getCharSet() != null))
    {
      Charset charset = contentType.getCharSet();
      for (JsonEncoding encoding : JsonEncoding.values()) {
        if (charset.name().equals(encoding.getJavaName())) {
          return encoding;
        }
      }
    }
    return JsonEncoding.UTF8;
  }
}
